#include "connection.h"

Connection::Connection(bool onlyReceive)
{
	this->onlyReceive = onlyReceive;
	sequence = 0;
	remote_sequence = 0;
	connectionState = QAbstractSocket::UnconnectedState;
	timer = new QTimer();
	timer->setInterval(20);
}

Connection::Connection(QHostAddress a, quint16 p, bool onlyReceive)
{
	this->onlyReceive = onlyReceive;
	address = a;
	port = p;
	bindPort = port;
	sequence = 0;
	remote_sequence = 0;

	if (!onlyReceive)
	{
		timer = new QTimer();
		timer->setInterval(20);
		startConnectionTimer(true);
		connect(timer, SIGNAL(timeout()), this, SLOT(packetCleanup()));
		lastPacketTime = time(0);

		tickNr = 0;
	}
	connectionState = QAbstractSocket::UnconnectedState;
}

void Connection::setAddress(QHostAddress addr)
{
	address = addr;
}

void Connection::setPort(quint16 p)
{
	port = p;
	bindPort = p;
}

void Connection::setOnlyReceive(bool onr)
{
	onlyReceive = onr;
}

void Connection::connToHost()
{
	sendQueue.clear();
	if (isLocalhostAddr())
		bindPort++;
	bind(bindPort, QUdpSocket::DontShareAddress);

	if (!onlyReceive)
	{

		connect(timer, SIGNAL(timeout()), this, SLOT(packetCleanup()));
		lastPacketTime = time(0);
		tickNr = 0;
	}

	if (isLocalhostAddr())
	{
		while (state() == QAbstractSocket::UnconnectedState)
		{
			bindPort++;
			bind(bindPort);
		}
	}

	if (state() == QAbstractSocket::BoundState)
	{
		if (onlyReceive)
			emit connectionStateChanged(QAbstractSocket::ListeningState);
		connect(this, SIGNAL(readyRead()), this, SLOT(onNewDatagram()));
	}
}

void Connection::disconnFromHost()
{
	//abort();
	close();
	sendQueue.clear();
	receiveList.clear();
	reliableSendList.clear();
	startConnectionTimer(false);
	connectionState = QAbstractSocket::UnconnectedState;
	emit connectionStateChanged(connectionState);
}

void Connection::onNewDatagram()
{
	while (hasPendingDatagrams())
	{
		QByteArray datagram;
		QHostAddress *host = new QHostAddress();
		quint16 *port = new quint16;
		datagram.resize(pendingDatagramSize());
		readDatagram(datagram.data(), datagram.size(), host, port);

		Packet *packet = new Packet(datagram);
		packet->setSenderAddress(*host);
		packet->setSenderPort(*port);
		delete port;
		delete host;
		emit newPacket(packet);
	}
}

void Connection::packetCleanup()
{
	//qDebug() << "cleanup";
	//qDebug() << time(0) << lastPacketTime;
	/*qDebug() << "rec size" << receiveList.count();
	 qDebug() << "rel size" << reliableSendList.count();
	 qDebug() << "snd size" << sendQueue.count();*/

	if ((time(0) - lastPacketTime) > 6)
	{
		disconnFromHost();
		return;
	}
	if (!reliableSendList.isEmpty())
	{
		quint32 minSequence;
		if (remote_sequence <= 32)
			minSequence = 0;
		else
			minSequence = remote_sequence - 32;
		quint32 now = time(0);
		for (int i = reliableSendList.count() - 1; i >= 0; i--)
		{
			Packet* reliablePacket = reliableSendList.at(i);
			if ((reliablePacket->getSequence() < minSequence) || (now
					- reliablePacket->getSendTime() >= 2))
			{
				//qDebug() << "lost 1" << reliablePacket->getSequence() << minSequence;
				sendQueue.push_front(reliablePacket);
				reliableSendList.remove(i);
			}
		}
	}

	if (!sendQueue.isEmpty())
	{
		Packet* packet = sendQueue.at(0);
		sendQueue.pop_front();
		sendPacket(packet);
	}

}

void Connection::sendPacket(Packet* packet)
{
	packet->setSequence(sequence);
	packet->setSendTime(time(0));
	packet->setProtocolId(12345);
	sequence++;
	if (packet->getIsReliable())
	{
		reliableSendList.append(packet);
	}

	packet->setAck(remote_sequence);
	QBitArray arr(32, false);
	quint32 minSequence;
	if (packet->getAck() <= 32)
		minSequence = 0;
	else
		minSequence = packet->getAck() - 32;
	for (int i = receiveList.count() - 1; i >= 0; i--)
	{
		Packet* recPacket = receiveList.at(i);
		if (recPacket->getSequence() < minSequence)
		{
			receiveList.remove(i);
			delete recPacket;
		}
		else if (recPacket->getSequence() != packet->getAck())
		{
			arr.setBit(packet->getAck() - recPacket->getSequence() - 1);
		}
	}
	packet->setAckBitfield(arr);
	writeDatagram(packet->toByteArray(), address, port);
	if (!packet->getIsReliable())
	{
		delete packet;
	}
}

void Connection::queuePacket(Packet* packet)
{
	if (!onlyReceive)
	{
		sendQueue.push_back(packet);
	}
}

void Connection::sendPacket(const QByteArray data, bool reliable, bool queue)
{
	Packet* packet = new Packet();
	PacketData* d = new PacketData();
	d->appendString(data.data());
	packet->setData(d);
	packet->setIsReliable(reliable);

	if (queue)
	{
		queuePacket(packet);
	}
	else
	{
		sendPacket(packet);
	}
}

void Connection::sendPacket(PacketData* data, bool reliable, bool queue)
{
	Packet* packet = new Packet();
	packet->setData(data);
	packet->setIsReliable(reliable);
	if (queue)
	{
		queuePacket(packet);
	}
	else
	{
		sendPacket(packet);
	}
	delete data;
}

void Connection::processPacket(Packet* packet)
{
	lastPacketTime = time(0);
	if (!onlyReceive)
	{
		remote_sequence = qMax(remote_sequence, packet->getSequence());

		QBitArray arr = packet->getAckBitfield();
		quint32 maxSequence = packet->getAck();
		quint32 minSequence;
		if (remote_sequence <= 32)
			minSequence = 0;
		else
			minSequence = remote_sequence - 32;
		quint32 now = time(0);
		for (int i = reliableSendList.count() - 1; i >= 0; i--)
		{
			Packet* reliablePacket = reliableSendList.at(i);
			quint32 bitNr = maxSequence - reliablePacket->getSequence() - 1;
			//if (((bitNr > 32) || (!arr.at(bitNr))) && (reliablePacket->getSequence() != maxSequence))
			if ((reliablePacket->getSequence() < minSequence) || (now
					- reliablePacket->getSendTime() >= 2))
			{
				//qDebug() << "lost 2";
				sendQueue.push_front(reliablePacket);

				reliableSendList.remove(i);
			}

			else
			{
				//qDebug() << "accepted";
				delete reliablePacket;
				reliableSendList.remove(i);
			}
		}

		receiveList << packet;
	}
	PacketData* packetData = new PacketData(packet->getData());
	runCommand(packetData, remote_sequence == packet->getSequence());
	delete packetData;
}

void Connection::startConnectionTimer(bool st)
{
	//qDebug() << "start stop" << st;
	if (!onlyReceive)
	{
		if (st && !timer->isActive())
		{
			timer->start();
		}
		else if (!st && timer->isActive())
		{
			//qDebug() << "timer stopped";
			timer->stop();
		}
	}
}

void Connection::flushSendQueue()
{
	foreach (Packet* packet, sendQueue)
		{
			writeDatagram(packet->toByteArray(), address, port);
		}
	sendQueue.clear();
}

QAbstractSocket::SocketState Connection::getConnectionState()
{
	return connectionState;
}

void Connection::runCommand(PacketData* data, bool isNewestPacket)
{
}

QString Connection::getHostPortKey()
{
	return address.toString() + ":" + QString::number(port);
}

bool Connection::isLocalhostAddr()
{
	return (address.toString() == "localhost") || (address.toString()
			== "127.0.0.1");
}
